package osDemo;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author chen zhe
 *
 * reader-writer problem demo
 */
public class ReaderWriterSource {
    private final Semaphore sourceMutex = new Semaphore(1); // 临界资源的锁

    private final Semaphore read = new Semaphore(0); // 写者优先，读者之后的同步锁

    private final Semaphore mutexr = new Semaphore(1); // 保证操作原子性的信号量
    private final Semaphore mutexw = new Semaphore(1); // 保证操作原子性的信号量

    private final Semaphore mutex = new Semaphore(1); // 用于实现waiting变量的互斥访问
    private final AtomicBoolean readerWaiting = new AtomicBoolean(false);

    private final AtomicInteger countr = new AtomicInteger(0);
    private final AtomicInteger countw = new AtomicInteger(0);

    private static final int TEMP = 20;

    public class Reader {
        public void read() throws InterruptedException {
            for (int i = 0; i < TEMP; i++) {
                mutex.acquire();
                if (countw.get() > 0) { // 判断当前是否有写者
                    readerWaiting.set(true);
                    read.acquire();
                }
                mutex.release();

                mutexr.acquire();
                if (countr.incrementAndGet() == 1) {
                    sourceMutex.acquire();
                }
                mutexr.release();

                System.out.println("*** reading source ***");

                mutexr.acquire();
                if (countr.decrementAndGet() == 0) {
                    sourceMutex.release();
                }
                mutexr.release();
            }
        }
    }

    public class Writer {
        // 真写者优先
        public void write() throws InterruptedException {
            for (int i = 0; i < TEMP; i++) {
                mutex.acquire();
                countw.incrementAndGet();
                mutex.release();

                sourceMutex.acquire();
                System.out.println("--- writing source ---");
                sourceMutex.release();

                mutexw.acquire();
                if (countw.decrementAndGet() == 0 && readerWaiting.get()) {
                    readerWaiting.set(false);
                    read.release();
                }
                mutexw.release();
            }
        }
    }

    public static class Test {
        public static void main(String[] args) {
            ReaderWriterSource source = new ReaderWriterSource();
            Writer writer = source.new Writer();
            Reader reader = source.new Reader();

            for (int i = 0; i < 6; ++i) { // 写者线程
                new Thread(() -> {
                    try { writer.write(); } catch (InterruptedException e) { e.printStackTrace(); }
                }).start();
            }
            for (int i = 0; i < 6; ++i) { // 读者线程
                new Thread(() -> {
                    try { reader.read(); } catch (InterruptedException e) { e.printStackTrace(); }
                }).start();
            }
        }
    }
}
